﻿using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Emgu.CV.Util;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace August.Core
{
    public class CV
    {
        public static CV Instance = new CV();

        public void Imread(string filename, out Mat matout, ImreadModes imreadModes)
        {
            matout = CvInvoke.Imread(filename, imreadModes);
            //imagePanel.DisplayImage = matout;
        }

        public void Threshold(Mat mat, int threshold, int maxthreshold, Emgu.CV.CvEnum.ThresholdType thresholdType, ref Mat matout)
        {
            Mat image = mat;
            double k = CvInvoke.Threshold(image, matout, 0, 255, thresholdType);
            //imagePanel.DisplayImage = matout;
        }

        public void BitwiseNot(Mat mat, ref Mat matout)
        {
            Mat image = mat;
            CvInvoke.BitwiseNot(image, matout);
            //imagePanel.DisplayImage = matout;
        }


        public void Erode(Mat mat, int radius, ref Mat matout)
        {
            Mat element = CvInvoke.GetStructuringElement(Emgu.CV.CvEnum.ElementShape.Rectangle, new Size(radius, radius), new Point(-1, -1));
            Mat element1 = CvInvoke.GetStructuringElement(Emgu.CV.CvEnum.ElementShape.Rectangle, new Size(5, 5), new Point(-1, -1));
            CvInvoke.Erode(mat, matout, element1, new Point(-1, -1), 1, Emgu.CV.CvEnum.BorderType.Default, new Emgu.CV.Structure.MCvScalar());
            CvInvoke.Dilate(matout, matout, element, new Point(-1, -1), 1, Emgu.CV.CvEnum.BorderType.Default, new Emgu.CV.Structure.MCvScalar());
            CvInvoke.Erode(matout, matout, element, new Point(-1, -1), 1, Emgu.CV.CvEnum.BorderType.Default, new Emgu.CV.Structure.MCvScalar());
        }

        public void CalcHist(Mat mat)
        {
            VectorOfMat vMatImg1 = new VectorOfMat();
            VectorOfPoint vectorOfPoint = new VectorOfPoint();
            int[] channels_1 = new int[] { 0 };
            Mat dHist_1 = new Mat();
            int[] histSize_1 = new int[] { 256 };       //bins(柱子个数)
            float[] range_1 = new float[] { 0, 256 };   //每一维数值的取值范围
            vMatImg1.Push(mat);
            CvInvoke.CalcHist(vMatImg1, channels_1, new Mat(), dHist_1, histSize_1, range_1, false);
        }


        public void FindRegion(Mat mat, out VectorOfVectorOfPoint vvp, out VectorOfPoint maxvp, ref Mat matout, int level = 100)
        {
            vvp = new VectorOfVectorOfPoint();
            Mat disp = new Image<Gray, byte>(mat.Width, mat.Height).Mat;
            byte[] bts = (byte[])mat.GetData(false);
            disp.SetTo(bts);
            int index = 0;
            Mat b1 = new Mat();
            CvInvoke.FindContours(mat, vvp, b1, Emgu.CV.CvEnum.RetrType.Tree, Emgu.CV.CvEnum.ChainApproxMethod.ChainApproxNone);
            List<double> areas = new List<double>();
            for (int i = 0; i < vvp.Size; i++)
            {
                double area = CvInvoke.ContourArea(vvp[i]);
                areas.Add(area);
            }
            double[] areasCopy = new double[areas.Count];
            areas.CopyTo(areasCopy);
            areas.Sort();
            List<double> areasCopyList = new List<double>(areasCopy);
            index = areasCopyList.IndexOf(areas[areas.Count - 1]);
            CvInvoke.DrawContours(disp, vvp, index, new MCvScalar(0), level);
            CvInvoke.FindContours(disp, vvp, b1, Emgu.CV.CvEnum.RetrType.Tree, Emgu.CV.CvEnum.ChainApproxMethod.ChainApproxNone);
            List<double> areas2 = new List<double>();
            for (int i = 0; i < vvp.Size; i++)
            {
                double area2 = CvInvoke.ContourArea(vvp[i]);
                areas2.Add(area2);
            }
            double[] areasCopy2 = new double[areas2.Count];
            areas2.CopyTo(areasCopy2);
            areas2.Sort();
            List<double> areasCopyList2 = new List<double>(areasCopy2);
            int num = areasCopyList2.IndexOf(areas2[areas2.Count - 1]);
            maxvp = vvp[num];
            index = num;
        }


        public void MinEnclosingCircle(VectorOfPoint vp, out CircleF circleF)
        {
            circleF = CvInvoke.MinEnclosingCircle(vp);
        }


        public bool Calib_FindChessboardCorners(Mat mat, Size size, out PointF[] pointF)
        {
            using (VectorOfPointF vectorOfPoint = new VectorOfPointF())
            {
                bool ret = CvInvoke.FindChessboardCornersSB(mat, size, vectorOfPoint, CalibCbType.Exhaustive | CalibCbType.Accuracy);
                pointF = vectorOfPoint.ToArray();
                return ret;
            }
        }

        public Mat Calib_FindHomography2D(PointF[] from, PointF[] to, out double rms)
        {
            Mat homMat2D = CvInvoke.FindHomography(from, to);
            PointF[] pointFsTo = CvInvoke.PerspectiveTransform(from, homMat2D);
            double[] offsets = new double[pointFsTo.Length];
            offsets = GetDistance(pointFsTo, to);
            double avg = offsets.Average();
            rms = offsets.Sum(x => Math.Pow(x - avg, 2)) / offsets.Length;
            return homMat2D;
        }

        private double[] GetDistance(PointF[] pointA, PointF[] pointB)
        {
            if (pointA.Length != pointB.Length) throw new Exception("getDistance : point length not equal");
            double[] distances = new double[pointA.Length];
            for (int i = 0; i < distances.Length; i++)
            {
                distances[i] = Math.Pow((pointA[i].X - pointB[i].X), 2) + Math.Pow((pointA[i].Y - pointB[i].Y), 2);
                distances[i] = Math.Sqrt(distances[i]);
            }
            return distances;
        }


        public PointF[] Calib_TransformHomoGraphy2D(PointF[] pointFs, Mat mat)
        {
            return CvInvoke.PerspectiveTransform(pointFs, mat);
        }

        public PointF[] Calib_GetCalibBoardPoints(Size size, float width, float height, PointF originPoint)
        {
            PointF[] pointFs = new PointF[size.Width * size.Height];
            for (int i = 0; i < size.Height; i++)
            {
                for (int j = 0; j < size.Width; j++)
                {
                    int index = i * size.Width + j;
                    pointFs[index].Y = originPoint.Y + i * height;
                    pointFs[index].X = originPoint.X + j * width;
                }
            }
            return pointFs;
        }


        public void CopyRectancleROI(Image src, ref Image des, Rectangle rect)
        {
            Bitmap bitmap = new Bitmap(src);
            Image<Gray, byte> img = new Image<Gray, byte>(bitmap);
            Range range1 = new Range(rect.Location.X, rect.Location.X + rect.Width);
            Range range2 = new Range(rect.Location.Y, rect.Location.Y + rect.Height);
            Mat mat = new Mat(img.Mat, range2, range1);
            des = mat.Bitmap;
        }


        public void MatchTemplate(Image src, Image tempImg, Image dst, int templateMatchingType)
        {
            Bitmap bitmap = new Bitmap(src);
            Image<Gray, byte> img = new Image<Gray, byte>(bitmap);
            Bitmap tempBitmap = new Bitmap(tempImg);
            Image<Gray, byte> temp = new Image<Gray, byte>(tempBitmap);
            Mat dstmat = new Mat(100, 100, DepthType.Cv32F, 1);
            CvInvoke.MatchTemplate(img, temp, dstmat, TemplateMatchingType.Sqdiff);
            CvInvoke.Imshow("dstMat", dstmat);
        }
    }
}
